package Model;
import java.util.*;
import java.util.Stack; //required for stack operations


public class TaskManager implements ITaskManager
{
	private ArrayList<Task> mTasks;
	private ArrayList<IObserver> mObservers;
	private Stack<Command> Commands;
	private Stack<Command> RedoCommands;
	
	public TaskManager()
	{
		mObservers = new ArrayList<IObserver>(5);
		mTasks = new ArrayList<Task>(100);
		Commands = new Stack<Command>();
		RedoCommands = new Stack<Command>();
	}
	
	public void notifyObservers()
	{
        for (IObserver mObserver : mObservers) {
            mObserver.update();
        }
	}
	
	public void registerObserver(IObserver ob)
	{
		if(null != ob)
		{
			mObservers.add(ob);
		}
		else
		{
			throw new NullPointerException("The observer is null!");
		}
		
	}

	public ArrayList<Task> getTasks() 
	{
		return mTasks;
	}

    /**
     *
     * @param taskName
     * @param taskDate
     * @param iPeriodic iPeriodic should be either "Single" OR "Periodic"
     * @param periodicFrequency periodicFrequency should be either Daily, Weekly or Monthly or null
     */
	public void addTask(String taskName, Date taskDate, String iPeriodic,String periodicFrequency)

	{
		AddTaskCommand AddCommand = new AddTaskCommand(mTasks);
		AddCommand.execute(taskName, taskDate, iPeriodic , periodicFrequency);
		Commands.push(AddCommand);
		resetRedoCommandStack();
		notifyObservers();		
	}

    public void addSubTask(String parentTaskName, String taskName, String iPeriodic, String periodicFrequency, Date iDate) throws Exception //iPeriodic should be either "Single" OR "Periodic"
                                                                                                               //periodicFrequency should be either Daily, Weekly or Monthly or null
    {
        AddSubTaskCommand SubAddCommand = new AddSubTaskCommand(mTasks);
		SubAddCommand.execute(parentTaskName, taskName, iPeriodic , periodicFrequency,iDate);
		Commands.push(SubAddCommand);
		resetRedoCommandStack();
		notifyObservers();

    }

	public void deleteTask(String taskName) 
	{
		// Search the task name in the list of parent tasks. if found, delete it
		boolean wFoundParent = false;
		for(int i = 0; i < mTasks.size(); ++i)
		{
			if(mTasks.get(i).getName().compareTo(taskName) == 0)
			{
				wFoundParent = true;
				break;
			}
		}
		if(wFoundParent)
		{
			DeleteTaskCommand DeleteCommand = new DeleteTaskCommand(mTasks);
			DeleteCommand.execute(taskName);
			Commands.push(DeleteCommand);
			resetRedoCommandStack();
			notifyObservers();
		}
		else
		{
			// delegate 
			deleteSubTask(taskName);
			
		}
	}

    public void deleteSubTask(String taskName)
    {
        DeleteSubTaskCommand SubDeleteCommand = new DeleteSubTaskCommand(mTasks);
        SubDeleteCommand.execute(taskName);
        Commands.push(SubDeleteCommand);
        resetRedoCommandStack();
        notifyObservers();
    }

	public void setTaskStatus(boolean isTaskResolved, String taskName)
	{
		// Search the task name in the list of parent tasks. if found, set  its status
		boolean wFoundParent = false;
		for(int i = 0; i < mTasks.size(); ++i)
		{
			if(mTasks.get(i).getName().compareTo(taskName) == 0)
			{
				wFoundParent = true;
				break;
			}
		}
		if(wFoundParent)
		{
			SetTaskStatusCommand SetStatusCommand = new SetTaskStatusCommand(mTasks);
			SetStatusCommand.execute(taskName, isTaskResolved);
			Commands.push(SetStatusCommand);
			resetRedoCommandStack();
			notifyObservers();
		}
		else
		{
			setSubTaskStatus(taskName, isTaskResolved);
		}
	}

    public void setSubTaskStatus(String taskName, boolean isTaskResolved)
    {
        SetSubTaskStatusCommand SetSubStatusCommand = new SetSubTaskStatusCommand(mTasks);
        SetSubStatusCommand.execute(taskName,isTaskResolved);
        Commands.push(SetSubStatusCommand);
        resetRedoCommandStack();
        notifyObservers();
    }
	
	public void undo()
	{
		Command ToBeUndoneCommand = Commands.pop();
		ToBeUndoneCommand.undo();
		RedoCommands.push(ToBeUndoneCommand);
		notifyObservers();
	}
	
	public void redo()
	{
		Command ToBeDoneCommand = RedoCommands.pop();
		ToBeDoneCommand.redo();
		Commands.push(ToBeDoneCommand);
		notifyObservers();
	}
	
	
	//empty the entire stack
	private void resetRedoCommandStack()
	{
		boolean flag=true;
		
		while(flag)
		{
			if(RedoCommands.empty()==true)
			{
				break;
			}
			
			RedoCommands.pop();	
		}//end of while
	}

}
